﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if SILVERLIGHT
using AbstraX.ClientInterfaces;
#else
using AbstraX.ServerInterfaces;
#endif
using AbstraX.Expressions;
using System.Reflection;
using System.Diagnostics;

namespace AbstraX.QueryProviders
{
    /// <summary>
    ///  Traced based on LINQ lambda expressions
    /// </summary>
    public abstract class ExpressionTraceableHierarchy<T> : PathTraceableHierarchy<T> where T : IBase
    {
        protected string parentID;

        public ExpressionTraceableHierarchy()
        {
        }

        public ExpressionTraceableHierarchy(string parentID)
        {
            this.parentID = parentID;
        }

        protected abstract IRoot Root { get; }

        protected override IQueryable<T> QueryResults
        {
            get 
            {
                if (parentID == null)
                {
                    return this.Root.RootElements.Cast<T>().AsQueryable<T>();
                }
                else
                {
                    return null;
                }
            }
        }

        public override IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        {
            lock (lockObject)
            {
                try
                {
                    var visitor = new AbstraXVisitor(expression);

                    if (visitor.BaseNode is MethodNode)
                    {
                        MethodNode methodNode = (MethodNode)visitor.BaseNode;

                        switch (methodNode.Method.Name)
                        {
                            case "Where":

                                var listItem = (ListItemNode)methodNode.Parameters.ElementAt(1).Value;

                                if (listItem.Value is BinaryExpressionNode)
                                {
                                    var node = (BinaryExpressionNode)listItem.Value;
                                    var listItemLeft = (ListItemNode)node.Left;
                                    var listItemRight = (ListItemNode)node.Right;

                                    if (listItemLeft.Value is MemberNode)
                                    {
                                        var memberNode = (MemberNode)listItemLeft.Value;
                                        var loop = 0;

                                        if (listItemRight.Value is PrimitiveNode)
                                        {
                                            IQueryable queryable = null;
                                            var primitiveNode = (PrimitiveNode)listItemRight.Value;
                                            string value = null;
                                            string filterField = null;
                                            var filterParent = false;

                                            if (primitiveNode.Value.GetType() == typeof(string))
                                            {
                                                value = (string)primitiveNode.Value;
                                            }
                                            else
                                            {
                                                var field = primitiveNode.Value.GetType().GetFields().Where(f => f.FieldType.Name == "String").First();

                                                filterField = field.Name;
                                                value = (string)field.GetValue(primitiveNode.Value);
                                            }

                                            var root = this.Root;

                                            root.ClearPredicates();

                                            var whereClauses = ProcessWhere(value);
                                            var where = whereClauses.Dequeue();

                                            root.ExecuteGlobalWhere(where);

                                            if (whereClauses.Count > 0)
                                            {
                                                if (filterField == root.ParentFieldName)
                                                {
                                                    filterParent = true;
                                                }
                                                else
                                                {
                                                    loop = 1;
                                                }

                                                where = whereClauses.Dequeue();

                                                root.ExecuteWhere(where);
                                            }

                                            var elements = root.RootElements.AsQueryable();
                                            Type type = null;

                                            if (listItemLeft.Value is MemberNode)
                                            {
                                                type = ((MemberNode)listItemLeft.Value).Member.DeclaringType;
                                            }

                                            queryable = elements;

                                            while (whereClauses.Count > loop)
                                            {
                                                where = whereClauses.Dequeue();
                                                var elementQueryable = queryable.Cast<IElement>();
                                                var element = elementQueryable.First();

                                                element.ClearPredicates();
                                                element.ExecuteWhere(memberNode.Member.Name, where);

                                                queryable = element.ChildElements.AsQueryable();
                                            }

                                            if (filterParent)
                                            {
                                                if (typeof(T) == typeof(IElement) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IElement"))
                                                {
                                                    IElement element = queryable.Cast<IElement>().First();

                                                    //element.ClearPredicates();
                                                    //element.ExecuteWhere(where);

                                                    queryable = element.ChildElements.AsQueryable();
                                                }
                                                else if (typeof(T) == typeof(IAttribute) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IAttribute"))
                                                {
                                                    IElement element = queryable.Cast<IElement>().First();

                                                    //element.ClearPredicates();
                                                    //element.ExecuteWhere(where);

                                                    queryable = element.Attributes.AsQueryable();
                                                }
                                                else if (typeof(T) == typeof(IOperation) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IOperation"))
                                                {
                                                    IElement element = queryable.Cast<IElement>().First();

                                                    //element.ClearPredicates();
                                                    //element.ExecuteWhere(where);

                                                    queryable = element.Operations.AsQueryable();
                                                }
                                            }
                                            else if (loop == 1)
                                            {
                                                if (whereClauses.Count > 0)
                                                {
                                                    where = whereClauses.Dequeue();
                                                }

                                                if (typeof(T) == typeof(IElement) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IElement"))
                                                {
                                                    IElement element = queryable.Cast<IElement>().First();

                                                    element.ClearPredicates();
                                                    element.ExecuteWhere(where);

                                                    queryable = element.ChildElements.AsQueryable();
                                                }
                                                else if (typeof(T) == typeof(IAttribute) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IAttribute"))
                                                {
                                                    IElement element = queryable.Cast<IElement>().First();

                                                    element.ClearPredicates();
                                                    element.ExecuteWhere(where);

                                                    queryable = element.Attributes.AsQueryable();
                                                }
                                                else if (typeof(T) == typeof(IOperation) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IOperation"))
                                                {
                                                    IElement element = queryable.Cast<IElement>().First();

                                                    element.ClearPredicates();
                                                    element.ExecuteWhere(where);

                                                    queryable = element.Operations.AsQueryable();
                                                }
                                            }

                                            if (type != null)
                                            {
                                                return queryable.Cast<IBase>().Where(e => AncestorTypes(e.GetType()).Any(t => t == type)).Cast<TElement>();
                                            }
                                            else
                                            {
                                                return queryable.Cast<TElement>();
                                            }
                                        }
                                    }
                                }

                                break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debugger.Break();
                }
            }

            throw new NotImplementedException();
        }

        private List<Type> AncestorTypes(Type type)
        {
            List<Type> ancestors = new List<Type>();
            Type baseType = type;

            while (baseType != null)
            {
                ancestors.Add(baseType);

                foreach (Type interfaceType in baseType.GetInterfaces())
                {
                    ancestors.Add(interfaceType);
                }

                baseType = baseType.BaseType;
            }

            return ancestors;
        }
        
        //public override IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        //{
        //    var visitor = new AbstraXVisitor(expression);

        //    if (visitor.BaseNode is MethodNode)
        //    {
        //        MethodNode methodNode = (MethodNode) visitor.BaseNode;

        //        switch (methodNode.Method.Name)
        //        {
        //            case "Where":

        //                var listItem = (ListItemNode)methodNode.Parameters.ElementAt(1).Value;

        //                if (listItem.Value is BinaryExpressionNode)
        //                {
        //                    var node = (BinaryExpressionNode)listItem.Value;
        //                    var listItemLeft = (ListItemNode) node.Left;
        //                    var listItemRight = (ListItemNode)node.Right;

        //                    if (listItemLeft.Value is MemberNode)
        //                    {
        //                        var memberNode = (MemberNode) listItemLeft.Value;

        //                        if (listItemRight.Value is PrimitiveNode)
        //                        {
        //                            IQueryable queryable = null;
        //                            var primitiveNode = (PrimitiveNode)listItemRight.Value;
        //                            string value = null;
        //                            string filterField = null;
        //                            bool bFilterParent = false;

        //                            if (primitiveNode.Value.GetType() == typeof(string))
        //                            {
        //                                value = (string) primitiveNode.Value;
        //                            }
        //                            else
        //                            {
        //                                var field = primitiveNode.Value.GetType().GetFields().First();

        //                                filterField = field.Name;
        //                                value = (string)field.GetValue(primitiveNode.Value);
        //                            }

        //                            var root = this.Root;

        //                            root.ClearPredicates();

        //                            var whereClauses = ProcessWhere(value);
        //                            var where = whereClauses.Dequeue();

        //                            root.ExecuteGlobalWhere(where);

        //                            if (whereClauses.Count > 0)
        //                            {
        //                                if (filterField == root.ParentFieldName)
        //                                {
        //                                    bFilterParent = true;
        //                                }

        //                                where = whereClauses.Dequeue();

        //                                root.ExecuteWhere(where);
        //                            }
                                    
        //                            var elements = root.RootElements.AsQueryable();

        //                            queryable = elements;

        //                            while (whereClauses.Count > 1)
        //                            {
        //                                where = whereClauses.Dequeue();
        //                                var elementQueryable = queryable.Cast<IElement>();
        //                                var count = elementQueryable.Count();

        //                                if (count > 0)
        //                                {
        //                                    if (count == 1)
        //                                    {
        //                                        IElement element = elementQueryable.First();

        //                                        element.ClearPredicates();
        //                                        element.ExecuteWhere(memberNode.Member.Name, where);

        //                                        queryable = element.ChildElements.AsQueryable();
        //                                    }
        //                                    else
        //                                    {
        //                                        throw new Exception("More than one result from an ancestor in a path traceable hierarchy is currently not supported.");
        //                                    }
        //                                }
        //                                else
        //                                {
        //                                    return null;
        //                                }
        //                            }

        //                            if (whereClauses.Count > 0)
        //                            {
        //                                where = whereClauses.Dequeue();

        //                                if (bFilterParent)
        //                                {
        //                                    IElement element = queryable.Cast<IElement>().First();

        //                                    element.ClearPredicates();
        //                                    element.ExecuteWhere(where);

        //                                    queryable = element.ChildElements.AsQueryable();
        //                                }
        //                                else
        //                                {
        //                                    if (typeof(T) == typeof(IElement) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IElement"))
        //                                    {
        //                                        IElement element = queryable.Cast<IElement>().First();

        //                                        element.ClearPredicates();
        //                                        element.ExecuteWhere(where);

        //                                        queryable = element.ChildElements.AsQueryable();
        //                                    }
        //                                    else if (typeof(T) == typeof(IAttribute) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IAttribute"))
        //                                    {
        //                                        IElement element = queryable.Cast<IElement>().First();

        //                                        element.ClearPredicates();
        //                                        element.ExecuteWhere(where);

        //                                        queryable = element.Attributes.AsQueryable();
        //                                    }
        //                                    else if (typeof(T) == typeof(IOperation) || typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IOperation"))
        //                                    {
        //                                        IElement element = queryable.Cast<IElement>().First();

        //                                        element.ClearPredicates();
        //                                        element.ExecuteWhere(where);

        //                                        queryable = element.Operations.AsQueryable();
        //                                    }
        //                                }
        //                            }
                                    
        //                            if (bFilterParent)
        //                            {
        //                                var count = queryable.Cast<IElement>().Count();

        //                                if (count == 1)
        //                                {
        //                                    if (typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IElement"))
        //                                    {
        //                                        IElement element = queryable.Cast<IElement>().First();

        //                                        queryable = element.ChildElements.AsQueryable();
        //                                    }
        //                                    else if (typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IAttribute"))
        //                                    {
        //                                        IElement element = queryable.Cast<IElement>().First();

        //                                        queryable = element.Attributes.AsQueryable();
        //                                    }
        //                                    else if (typeof(T).GetInterfaces().Cast<Type>().Any(t => t.Name == "IOperation"))
        //                                    {
        //                                        IElement element = queryable.Cast<IElement>().First();

        //                                        queryable = element.Operations.AsQueryable();
        //                                    }
        //                                }
        //                                else if (count > 1)
        //                                {
        //                                    throw new Exception("More than one result from an ancestor in a path traceable hierarchy is currently not supported.");
        //                                }
        //                                else
        //                                {
        //                                    return new List<TElement>().AsQueryable();
        //                                }
        //                            }

        //                            return queryable.Cast<TElement>();
        //                        }
        //                    }
        //                }

        //                break;
        //        }
        //    }

        //    throw new NotImplementedException();
        //}

        public override IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {
            var visitor = new AbstraXVisitor(expression);

            throw new NotImplementedException();
        }

        public override TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
        {
            var visitor = new AbstraXVisitor(expression);

            throw new NotImplementedException();
        }

        public override object Execute(System.Linq.Expressions.Expression expression)
        {
            var visitor = new AbstraXVisitor(expression);

            throw new NotImplementedException();
        }
    }
}
